﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

using ABCat.Core.Plugins.DataSets;
using ABCat.Shared.Properties;

namespace ABCat.Core.Plugins.Catalog.FilteringLogics
{
	public abstract class FilteringLogicPluginBase : IFilteringLogicPlugin
	{
		#region + Events +

		public event EventHandler Disposed;
		public event PropertyChangedEventHandler PropertyChanged;

		#endregion
		#region + Fields +

		[Browsable(false)]
		public readonly Context Context;

		private bool _enabled = true;
		private volatile bool _isOnUpdate;

		#endregion
		#region + Properties +

		[Browsable(false)]
		public Config Config		{get; set;}
		[Browsable(false)]
		public abstract bool IsEmpty		{get;}
		[Category("Фильтр")]
		[DisplayName("Применить")]
		[Description("Применить фильтр")]
		public bool IsEnabled
		{
			get { return _enabled; }
			set
			{
				if (value.Equals(_enabled)) return;
				_enabled = value;
				OnPropertyChanged();
			}
		}

		[Browsable(false)]
		public virtual bool IsOnUpdate
		{
			get { return _isOnUpdate; }
			set
			{
				if (value.Equals(_isOnUpdate)) return;
				_isOnUpdate = value;
				OnPropertyChanged();
			}
		}

		#endregion
		#region + Ctor +

		protected FilteringLogicPluginBase(Context context)
		{
			Context = context;
		}

		#endregion
		#region + Logic +

		public virtual void BeginFilterAsync(IEnumerable<IAudioBook> records, Action<IEnumerable<IAudioBook>, Exception> completedCallback, CancellationToken cancellationToken)
		{
			Task.Factory.StartNew(
				() =>
				{
					try
					{
						IsOnUpdate = true;
						Thread.Sleep(500);
						if (cancellationToken.IsCancellationRequested) return;

						IAudioBook[] result = null;
						try
						{
							IsOnUpdate = true;
							result = records.TakeWhile(record => !cancellationToken.IsCancellationRequested).Where(FilterRecord).ToArray();
						}
						finally
						{
							IsOnUpdate = false;
						}

						completedCallback(result, null);
					}
					catch (Exception ex)
					{
						completedCallback(null, ex);
					}
					finally
					{
						IsOnUpdate = false;
					}
				}, cancellationToken);
		}

		public virtual void BeginUpdateCacheAsync(UpdateTypeEnum updateType, Action<Exception> completedCallback)
		{
			Task.Factory.StartNew(
				() =>
					{
						try
						{
							IsOnUpdate = true;
							UpdateCacheInternal(updateType);
							completedCallback(null);
						}
						catch (Exception ex)
						{
							completedCallback(ex);
						}
						finally
						{
							IsOnUpdate = false;
						}
					}
				);
		}

		public abstract bool CheckForConfig(bool correct, out Config incorrectConfig);

		public virtual void Dispose()
		{
			Disposed.Fire(this, EventArgs.Empty);
		}

		public abstract bool FilterRecord(IAudioBook record);

		[NotifyPropertyChangedInvocator]
		protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
		{
			PropertyChangedEventHandler handler = PropertyChanged;
			if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
		}

		protected abstract void UpdateCacheInternal(UpdateTypeEnum updateType);

		#endregion
	}
}